package com.example.shop.common.filter;

import com.example.shop.common.api.ResponseResult;
import com.example.shop.common.api.ServiceStateCode;
import com.example.shop.redis.enums.RedisKey;
import com.example.shop.redis.service.LettuceClient;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;

/**
 * 使用redis的lua脚本，对ip进行限流，阻止所有恶意请求
 */
@Component
@WebFilter(filterName = "MaliciousRequestFilter",urlPatterns = "/*")
public class RequestFilter implements Filter {

    @Autowired
    private LettuceClient lettuceClient;
    @Resource(name = "redisObjectMapper")
    private ObjectMapper objectMapper;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    /**
     * 如果同一个ip地址5秒连续访问内对服务器，请求超过13次，则会拦截该ip的所有请求5分钟
     *
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        //如果是恶意请求，则限制该ip地址不能请求本网站5分钟
        DefaultRedisScript<Boolean> script = new DefaultRedisScript<>();
        script.setScriptSource(new ResourceScriptSource(new ClassPathResource("resources\\lua\\request_filter.lua")));
        script.setResultType(Boolean.class);
        ArrayList<String> keys = new ArrayList<>();
        keys.add(RedisKey.IP_CURRENT_LIMIT+req.getRemoteHost());
        /**
         * 这里有个坑是：lua脚本执行，
         * incr：这个跟redis的一样，没有则自动创建一个k-v，返回增长后的结果
         * expire：这个就不一样了，设置过期时间后，时间到了会变成0，而不是变成-2
         * ttl：这个跟redis的一样，获取过期时间，没有过期时间，则为-1
         */
        Boolean notFilter= lettuceClient.execLua(script, keys, 25,5,300);
        if(notFilter){
            //不过滤，流向下一个过滤器
            chain.doFilter(request,response);
        }else{
            //如果是恶意请求，则限制该ip地址不能请求本网站5分钟
            response.setContentType("application/json; charset=utf-8");
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(ResponseResult.failure(ServiceStateCode.DISABLE_MALICIOUS_REQUEST));
            response.getWriter().write(json);
        }
    }

    @Override
    public void destroy() {
    }

}
